package com.github.smartheye.fabric.common.crypto;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.security.AlgorithmParameters;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;

import javax.crypto.Cipher;
import javax.crypto.EncryptedPrivateKeyInfo;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;

import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo;

public class PEMUtil {

    public static PublicKey publicKeyFromPEM(String pemStr) throws IOException {

        PEMParser pemParser = SecurityManager.newPEMParser(pemStr);
        try {
            Object o = pemParser.readObject();
            if (o == null) {
                throw new IOException("Not an OpenSSL key");
            }
            if (o instanceof PEMKeyPair) {
                KeyPair kp = SecurityManager.newJcaPEMKeyConverter().getKeyPair((PEMKeyPair) o);
                return kp.getPublic();
            } else if (o instanceof X509CertificateHolder) {
                X509CertificateHolder x509 = (X509CertificateHolder) o;
                return SecurityManager.newJcaPEMKeyConverter().getPublicKey(x509.getSubjectPublicKeyInfo());
            }
            throw new IOException("Not an OpenSSL key" + o);
        } finally {
            pemParser.close();
        }
    }

    public static PrivateKey privateKeyFromPEM(String pemStr) throws IOException {

        PEMParser pemParser = SecurityManager.newPEMParser(pemStr);
        try {
            Object o = pemParser.readObject();
            if (o == null) {
                throw new IOException("Not an OpenSSL key");
            }
            if (o instanceof PEMKeyPair) {
                KeyPair kp = SecurityManager.newJcaPEMKeyConverter().getKeyPair((PEMKeyPair) o);
                return kp.getPrivate();
            } else if (o instanceof PrivateKeyInfo) {
                JcaPEMKeyConverter converter = SecurityManager.newJcaPEMKeyConverter();
                return converter.getPrivateKey((PrivateKeyInfo) o);
            } else if (o instanceof PKCS8EncryptedPrivateKeyInfo) {
                PKCS8EncryptedPrivateKeyInfo eki = (PKCS8EncryptedPrivateKeyInfo) o;
                return privateKeyFromEncryptedPEM(eki.getEncoded(), "123456");
            }
            throw new IOException("Not an OpenSSL key" + o);
        } finally {
            pemParser.close();
        }
    }

    public static PrivateKey privateKeyFromEncryptedPEM(byte[] keyBytes, String password) throws IOException {
        EncryptedPrivateKeyInfo encryptPKInfo = new EncryptedPrivateKeyInfo(keyBytes);
        Cipher cipher;
        try {
            cipher = Cipher.getInstance(encryptPKInfo.getAlgName());
            PBEKeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray());
            SecretKeyFactory secFac = SecretKeyFactory.getInstance(encryptPKInfo.getAlgName());
            Key pbeKey = secFac.generateSecret(pbeKeySpec);
            AlgorithmParameters algParams = encryptPKInfo.getAlgParameters();
            cipher.init(Cipher.DECRYPT_MODE, pbeKey, algParams);
            KeySpec pkcs8KeySpec = encryptPKInfo.getKeySpec(cipher);
            KeyFactory kf = KeyFactory.getInstance("ECDSA");
            return kf.generatePrivate(pkcs8KeySpec);
        } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
                | InvalidAlgorithmParameterException | InvalidKeySpecException e) {
            e.printStackTrace();
        }
        return null;
    }

  
    public static final String publicKeyToPEM(PublicKey publicKey) {
        try {
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            JcaPEMWriter pemWriter = new JcaPEMWriter(new OutputStreamWriter(bos));
            pemWriter.writeObject(publicKey);
            pemWriter.close();
            return new String(bos.toByteArray());
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static final String privateKeyToPEM(PrivateKey privateKey) {
        try {
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            JcaPEMWriter pemWriter = new JcaPEMWriter(new OutputStreamWriter(bos));
            pemWriter.writeObject(privateKey);
            pemWriter.close();
            return new String(bos.toByteArray());
        } catch (IOException e) {
            // 无法抵达的异常
            e.printStackTrace();
        }
        return null;
    }
}
